#include <stdlib.h>
#include <string.h>

#include "elftool.h"


#define EI_NIDENT 16

typedef struct
{
	unsigned char e_ident[EI_NIDENT];     /* Magic number and other info */
	unsigned short    e_type;                 /* Object file type */
	unsigned short    e_machine;              /* Architecture */
	unsigned int    e_version;              /* Object file version */
	unsigned int    e_entry;                /* Entry point virtual address */
	unsigned int     e_phoff;                /* Program header table file offset */
	unsigned int     e_shoff;                /* Section header table file offset */
	unsigned int    e_flags;                /* Processor-specific flags */
	unsigned short    e_ehsize;               /* ELF header size in bytes */
	unsigned short    e_phentsize;            /* Program header table entry size */
	unsigned short    e_phnum;                /* Program header table entry count */
	unsigned short    e_shentsize;            /* Section header table entry size */
	unsigned short    e_shnum;                /* Section header table entry count */
	unsigned short    e_shstrndx;             /* Section header string table index */
} Elf32_Ehdr;

typedef struct
{
	unsigned int    p_type;                 /* Segment type */
	unsigned int     p_offset;               /* Segment file offset */
	unsigned int    p_vaddr;                /* Segment virtual address */
	unsigned int    p_paddr;                /* Segment physical address */
	unsigned int    p_filesz;               /* Segment size in file */
	unsigned int    p_memsz;                /* Segment size in memory */
	unsigned int    p_flags;                /* Segment flags */
	unsigned int    p_align;                /* Segment alignment */
} Elf32_Phdr;

Elf32_Ehdr hdr;

void elf_load_header(FILE* f) {
	fseek(f, 0, SEEK_SET);
	fread(&hdr, sizeof(Elf32_Ehdr), 1, f);
}


unsigned int reverse_uint(unsigned int o) {
	union {
		unsigned int i;
		unsigned char c[4];
	} ita;
	ita.i = o;

	unsigned char t = ita.c[0];
	
	ita.c[0] = ita.c[3];
	ita.c[3] = t;
	t = ita.c[1];
	ita.c[1] = ita.c[2];
	ita.c[2] = t;
	return ita.i;
}

unsigned short reverse_ushort(unsigned short o) {
	union {
		unsigned short i;
		unsigned char c[2];
	} ita;
	ita.i = o;
	unsigned char t = ita.c[0];
	ita.c[0] = ita.c[1];
	ita.c[1] = t;

	return ita.i;
}

unsigned int elf_entrypoint(FILE* f) {
	return reverse_uint(hdr.e_entry);
}

exec_section* elf_load_exec_section(FILE* f) {
	unsigned int phoff = reverse_uint(hdr.e_phoff);	
	unsigned int phsize = reverse_ushort(hdr.e_phentsize);
	unsigned int phnum = reverse_ushort(hdr.e_phnum);
	int i;

	// TODO: don't set static size!
	exec_section* es = (exec_section*)calloc(sizeof(exec_section), 16);
	memset(es, 0, sizeof(exec_section) * 16);

//	printf( "%x %x %x\n", phoff, phsize, phnum );

	int ecount = 0;
	for (i = 0; i < phnum; i++ ) {
		Elf32_Phdr ph;
		fseek(f, phoff+phsize*i, SEEK_SET);
		fread(&ph, sizeof(Elf32_Phdr), 1, f);

		unsigned int p_offset = reverse_uint(ph.p_offset);
		unsigned int p_vaddr = reverse_uint(ph.p_vaddr);
		unsigned int p_memsz = reverse_uint(ph.p_memsz);
		unsigned int p_filesz = reverse_uint(ph.p_filesz);
		unsigned int p_flags = reverse_uint(ph.p_flags);

		if (!(p_flags&1)) continue; // PF_X (executable) flag set

		if ( p_filesz != p_memsz ) {
			fprintf(stderr, "%x != %x\n", p_memsz, p_filesz);
			continue;
		}
		
		//TODO: don't have static sizes!! 
		if ( ecount >= 16 - 1 ) { // needs zero at the end
			fprintf(stderr, "number of executable sections exceed 16!!\n" );
			continue;
		}

//		es[ecount] = (exec_section)malloc(sizeof(exec_section));
		unsigned int* data = (unsigned int*)malloc(p_filesz);

		fseek(f, p_offset, SEEK_SET);
		fread(data, p_filesz, 1, f);
		es[ecount].offset = p_vaddr;
		es[ecount].size = p_memsz;
		es[ecount].data = data;
		printf( "es-- %x %x\n", p_vaddr, p_memsz );
		ecount++;

	}

	return es;
}


unsigned int e_phoff() {
	return hdr.e_phoff;
}


